package dnet

import (
	"bufio"
	"dgo/goutils/utils"
	"fmt"
	"net"
	"os"
	"strings"
)

type DNetFileServConnHandler struct {
	owner      *DNetFileServ
	conn       net.Conn
	user, pass string
	writer     *bufio.Writer
	reader     *bufio.Reader
	Tag        interface{}
}

func NewFileServConnHandler(owner *DNetFileServ, conn net.Conn) *DNetFileServConnHandler {
	rval := &DNetFileServConnHandler{owner: owner}
	rval.conn = conn
	rval.reader = bufio.NewReader(conn)
	rval.writer = bufio.NewWriter(conn)
	return rval
}

func (this *DNetFileServConnHandler) writeMessage(code int, msg string) error {
	str := fmt.Sprintf("%d %s\r\n", code, msg)
	this.writer.WriteString(str)
	return this.writer.Flush()
}

func (this *DNetFileServConnHandler) clientHandleEntry() {
	for {
		line, isPrefix, err := this.reader.ReadLine()
		if err != nil {
			break
		}
		if isPrefix {

		}
		this.handleCommand(string(line))
	}
}

func (this *DNetFileServConnHandler) handleCommandAuth(param string) {
	this.user, this.pass = Split2(param, ":")
	fn := this.owner.AuthEvent
	if fn != nil {
		if !fn(this, this.user, this.pass) {
			this.writeMessage(FS_STATUS_AUTH_FAIL, "auth err")
			return
		}
	}
	this.writeMessage(FS_STATUS_OK, "ok")
}

/*
**
上传文件
*/
func (this *DNetFileServConnHandler) handleCommandSTORE(param string) {
	s1, s2 := Split2(param, " ")
	filesize := utils.StrToInt64Def(s1, 0)
	realfilename := this.owner.GetRealFileName(this, FS_CMD_STORE, s2)
	if len(realfilename) == 0 {
		this.writeMessage(FS_STATUS_CMD_FAIL, "store filename is empty!")
		return
	}

	utils.ForceCreateFilePath(realfilename)

	wr, e1 := os.OpenFile(realfilename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm)
	if e1 != nil {
		this.writeMessage(FS_STATUS_CMD_FAIL, fmt.Sprintf("store filename err:%s", e1.Error()))
		return
	}
	defer wr.Close()
	this.writeMessage(FS_STATUS_OK, "ok")
	maxblock := this.owner.GetStoreFileBlock()
	remain := filesize
	for {
		blocksize := remain
		if blocksize > maxblock {
			blocksize = maxblock
		}
		buf := make([]byte, blocksize)
		n, err := this.reader.Read(buf)
		if err != nil {
			this.writeMessage(FS_STATUS_CMD_FAIL, "read err!!!")
			return
		}
		n1, e1 := wr.Write(buf[:n])
		if e1 != nil {
			this.writeMessage(FS_STATUS_CMD_FAIL, "write file err!!!")
			return
		}
		if n1 != n {
			this.writeMessage(FS_STATUS_CMD_FAIL, "write less err!!!")
			return
		}
		remain -= int64(n)
		if remain == 0 {
			break
		}
	}
	this.writeMessage(FS_STATUS_OK, "ok")
}

/*
**
删除文件

>> AUTH USER:PASS\r\n
<< 200 ok\r\n
>> DEL FILE\r\n
<< 200 ok\r\n
*/
func (this *DNetFileServConnHandler) handleCommandDEL(param string) {
	realfilename := this.owner.GetRealFileName(this, "DEL", param)
	if len(realfilename) == 0 {
		this.writeMessage(FS_STATUS_CMD_FAIL, "filename is empty!")
		return
	}

	ft := utils.FileIs(realfilename)
	if ft == 1 { //文件
		e1 := utils.DeleteFileOrDir(realfilename)
		if e1 != nil {
			this.writeMessage(FS_STATUS_CMD_FAIL, fmt.Sprintf("delete file err:%s", e1.Error()))
			return
		}
		this.writeMessage(FS_STATUS_OK, "ok")
	} else if ft == 0 {
		this.writeMessage(FS_STATUS_OK, "ok")
	} else {
		this.writeMessage(FS_STATUS_CMD_FAIL, "file is directory")
	}
}

/*
**
下载文件
>> RETR 0 a\b\test.txt\r\n        // 0 表示开始位置, -1 表示只获取文件大小
>> 213 FILESIZE\r\n
>> [FILE DATA]
<< 200 ok\r\n
*/
func (this *DNetFileServConnHandler) handleCommandRETR(param string) {
	s1, s2 := Split2(param, " ")
	posi := utils.StrToInt64Def(s1, 0)
	realfilename := this.owner.GetRealFileName(this, FS_CMD_RETR, s2)
	if len(realfilename) == 0 {
		this.writeMessage(FS_STATUS_CMD_FAIL, "filename is empty!")
		return
	}

	if posi == -1 { // 读取文件大小
		if !utils.FileIsExists(realfilename) {
			this.writeMessage(FS_STAUS_FILE, fmt.Sprintf("%d", -1))
			return
		} else {
			fsize, _ := utils.FileSize(realfilename)
			this.writeMessage(FS_STAUS_FILE, fmt.Sprintf("%d", fsize))
			return
		}
	} else if !utils.FileIsExists(realfilename) {
		this.writeMessage(FS_STATUS_CMD_FAIL, "文件不存在!")
		return
	}

	rd, e1 := os.OpenFile(realfilename, os.O_RDONLY, 0)
	if e1 != nil {
		this.writeMessage(FS_STATUS_CMD_FAIL, fmt.Sprintf("open file err:%s", e1.Error()))
		return
	}
	defer rd.Close()
	fi, e1 := rd.Stat()
	if e1 != nil {
		this.writeMessage(FS_STATUS_CMD_FAIL, fmt.Sprintf("stat file err:%s", e1.Error()))
		return
	}

	remain := fi.Size()

	if posi > 0 {
		if posi >= remain {
			this.writeMessage(FS_STAUS_FILE, "0")
			this.writeMessage(FS_STATUS_OK, "ok")
			return
		}
		rd.Seek(posi, 0)
		remain -= posi
	}

	this.writeMessage(FS_STAUS_FILE, fmt.Sprintf("%d", remain))

	maxblock := this.owner.GetDownFileBlock()
	for {
		blocksize := remain
		if blocksize > maxblock {
			blocksize = maxblock
		}
		buf := make([]byte, blocksize)
		n, err := rd.Read(buf)
		if err != nil {
			this.writeMessage(FS_STATUS_CMD_FAIL, "read file err!!!")
			return
		}
		n1, e1 := this.conn.Write(buf[:n])
		if e1 != nil {
			this.writeMessage(FS_STATUS_CMD_FAIL, "send file block err!!!")
			return
		}
		if n1 != n {
			this.writeMessage(FS_STATUS_CMD_FAIL, "send less err!!!")
			return
		}
		remain -= int64(n)
		if remain == 0 {
			break
		}
	}
	this.writeMessage(FS_STATUS_OK, "ok")
}

/*
**
下载文件
>> COPY a\b\test.txt new.txt\r\n
>> 200 ok\r\n
*/
func (this *DNetFileServConnHandler) handleCommandCOPY(param string) {
	s1, s2 := Split2(param, " ")
	srcfilename := this.owner.GetRealFileName(this, FS_CMD_COPY, s1)
	newfilename := this.owner.GetRealFileName(this, FS_CMD_COPY, s2)

	if len(srcfilename) == 0 {
		srcfilename = s1
	}

	if len(newfilename) == 0 {
		newfilename = s2
	}

	_, err := utils.CopyFile(srcfilename, newfilename)
	if err != nil {
		this.writeMessage(FS_STATUS_CMD_FAIL, err.Error())
	} else {
		this.writeMessage(FS_STATUS_OK, "ok")
	}

}

func (this *DNetFileServConnHandler) handleCommand(cmd string) {
	command, param := Split2(cmd, FS_CMD_SEP)
	command = strings.ToUpper(command)
	if command == FS_CMD_AUTH {
		this.handleCommandAuth(param)
	} else if command == FS_CMD_STORE {
		this.handleCommandSTORE(param)
	} else if command == FS_CMD_DEL {
		this.handleCommandDEL(param)
	} else if command == FS_CMD_RETR {
		this.handleCommandRETR(param)
	} else if command == FS_CMD_COPY {
		this.handleCommandCOPY(param)
	} else {
		this.writeMessage(FS_STATUS_CMD_FAIL, fmt.Sprintf("UNKOWN CMD[%s]", command))
	}
}
